/*
 *  Matrice.cpp
 *  
 *
 *  Created by Hélène Beaumont on 12/12/08.
 *  Copyright 2008 Personnel. All rights reserved.
 *
 */

#include <iostream>
#include <gmpxx.h>
#include <stdlib.h>
#include <fstream>

#include "Matrice.hpp"
#include "Factor.hpp"
#include "Polynome.hpp"

using namespace std;


void Matrice::buildMatrix(Factor & RFB, Factor & AFB, Factor & QFB, Factor & pairesAB, Polynome & p)
{
   int         nbAB    = pairesAB.getTaille();
   int         nbRFB   = RFB.getTaille();
   int         nbAFB   = AFB.getTaille();
   int         nbQFB   = QFB.getTaille();
   
   int         iAB;
   int         iRFB;
   int         iAFB;
   int         iQFB;
   
   int         pow;
   
   mpz_class   nb;
   mpz_class   nbPow;
   
   
   for(iAB = 0 ; iAB < nbAB ; iAB++)
   {
      nb = pairesAB.getPaire(iAB).getPaire(0) + pairesAB.getPaire(iAB).getPaire(1) * p.getM();
      
      if (nb < 0)
      {
         setMatrice(iAB, 0, 1);
      }
     
      
      for(iRFB = 0 ; iRFB < nbRFB ; iRFB++)
      {
         pow = 0;
         nbPow = RFB.getPaire(iRFB).getPaire(0);
         if(nb != 0)
         {
            while(mpz_divisible_p(nb.get_mpz_t(), nbPow.get_mpz_t()))
            {
               
               pow++;
//                
               nb /= RFB.getPaire(iRFB).getPaire(0);
            }
         }
         
         if(pow % 2)
         {
            setMatrice(iAB, 1 + iRFB, true);
         }
         
      }

         mpz_class a = pairesAB.getPaire(iAB).getPaire(0);
         mpz_class b = pairesAB.getPaire(iAB).getPaire(1);
         nb = p.bpowdf(a, b);
         
      for(iAFB = 0 ; iAFB < nbAFB ; iAFB++)
      {
         pow = 0;
         nbPow = AFB.getPaire(iAFB).getPaire(0);

         if(nb != 0)
         {
            while(mpz_divisible_p(nb.get_mpz_t(), nbPow.get_mpz_t()))
            {
               mpz_class calcul;
               mpz_class mod;
               
               calcul = pairesAB.getPaire(iAB).getPaire(0) + pairesAB.getPaire(iAB).getPaire(1) * AFB.getPaire(iAFB).getPaire(1);
               mpz_mod(mod.get_mpz_t(),calcul.get_mpz_t(), AFB.getPaire(iAFB).getPaire(0).get_mpz_t());
               while(mod != 0)
               {
                  iAFB++;
                  calcul = pairesAB.getPaire(iAB).getPaire(0) + pairesAB.getPaire(iAB).getPaire(1) * AFB.getPaire(iAFB).getPaire(1);
                  mpz_mod(mod.get_mpz_t(),calcul.get_mpz_t(), AFB.getPaire(iAFB).getPaire(0).get_mpz_t());
               }
               
               pow++;
               nb /= AFB.getPaire(iAFB).getPaire(0);
            }

         }
         
         if(pow % 2)
         {
            setMatrice(iAB, iAFB + nbRFB + 1, true);
         }
      }

      for(iQFB = 0 ; iQFB < nbQFB ; iQFB++)
      {
         mpz_class a;
         mpz_class b;
         
         a = pairesAB.getPaire(iAB).getPaire(0) + pairesAB.getPaire(iAB).getPaire(1) * QFB.getPaire(iQFB).getPaire(1);
         b = QFB.getPaire(iQFB).getPaire(0);
         
         int legendre = mpz_kronecker(a.get_mpz_t(), b.get_mpz_t());
         
         if(legendre != 1)
            setMatrice(iAB, iQFB + nbRFB + nbAFB + 1, 1);

      }

  
   }

}

Matrice Matrice::trans()
{
   Matrice mat(_nbColonnes, _nbLignes);
   int i = 0;
   int j = 0;
   
   for( ; i < _nbLignes ; i++)
   {
      for(j = 0 ; j < _nbColonnes ; j++)
      {
         mat.setMatrice(j, i, getMatrice(i, j));
      }
   }
   
   return mat;
}




// i = i -j
void Matrice::sub(int inI, int inJ)
{
   for(int i = 0 ; i < _nbColonnes ; i++)
   {
      _matrice[ inI * _nbColonnes + i ] ^= _matrice[ inJ * _nbColonnes + i]; 
   }
}

// Soustraie la ligne i a toutes les lignes en dessous de i
void Matrice::supUnder(int inI)
{
   for(int i = inI + 1 ; i < _nbLignes ; i++)
   {
      if(_matrice[i * _nbColonnes + inI])
         sub(i, inI);
   }
}

// Soustraie la ligne i a toutes les lignes au dessus de i
void Matrice::supOver(int inJ, int inK)
{
   for(int i = 0 ; i < inJ ; i++)
   {
      if(_matrice[i * _nbColonnes + inK])
         sub(i, inJ);
   }
}

bool * Matrice::Gauss()
{   
   for(int i = 0 ; i < _nbColonnes ; i++)
   {
      _vectFree[i] = 0;
   }
   
   for(int i = 0 ; i < _nbLignes ; i++)
   {
      int j = i;
      // Tant qu'on ne trouve pas de valeur sur la colonne en dessous de la ligne i
      while( j < _nbLignes &&  ! (_matrice[j * _nbColonnes + i])) j++;
     
      if(j < _nbLignes) // On a une valeur non nulle
      {
         if( j != i)
         {
            swapLignes(i, j);
         }
         
         supUnder(i);
      }
   }
   
   
   _vectFree[0] = 0;
   int k = 0;
   int iMax = 0;
   
   for(int j = 1 ; j < _nbLignes ; j++)
   {
      k = j;
     
      //cout << j << endl;
      // Recherche du premier 1 rencontre de la ligne j
      while(_matrice[j * _nbColonnes + k] != 1 && k < _nbColonnes)
      {
         if(k > iMax)
            _vectFree[k] = 1;
         k++;
      }
          
      if(k < _nbColonnes)
      {
         supOver(j, k);
      
         iMax = k;
         _vectFree[k] = 0;
      }
   }
//    for(int i = 0 ; i < 72 ; i++)
//       cout << vectFree[i] << " ";
           
      return _vectFree;
}


void Matrice::generateFile()
{
   ofstream fic;
   
   fic.open("notregnfs.mat", ios::out);
   
   if(fic.bad())
   {
      exit(-1);
   }
   
   fic << "[";
   // Remplissage des nombres premiers
   for(int i = 0 ; i < _nbLignes - 1 ; i++)
   {
      fic << "[";
      for(int j = 0 ; j < _nbColonnes - 1 ; j++)
      {
         fic << getMatrice(i, j) << " ";
      }
      fic << getMatrice(i, _nbColonnes - 1) << "]" << endl;
   }
   
   // Dernière ligne
   fic << "[";
   for(int j = 0 ; j < _nbColonnes - 1 ; j++)
   {
      fic << getMatrice(_nbLignes - 1, j) << " ";
   }
   fic << getMatrice(_nbLignes - 1, _nbColonnes - 1) << "]" << endl << "]" << endl;
   
   // Ecriture du vecteur free
   fic << "[";
   for(int j = 0 ; j < _nbColonnes - 1 ; j++)
   {
      fic << _vectFree[j] << " ";
   }
   fic << _vectFree[_nbColonnes - 1] << "]";
         
   fic.close();
}

ostream & operator<< (ostream & o, const Matrice & inMatrice)
{
   for(int x = 0 ; x < inMatrice.getNbLignes() ; x++)
   {
      for(int y = 0 ; y < inMatrice.getNbColonnes() ; y++)
      {
         o << inMatrice.getMatrice(x, y) << " ";
      }
      o << endl;
   }
   return o;
}

